package projet.volleystat.automate;

import java.util.ArrayList;
import java.util.Map;
import java.util.TreeMap;

import projet.volleystat.Player;
import projet.volleystat.Team;
import android.util.SparseArray;

/**
 * @author Lameira Nelaupe Ferrand Fiore
 * @version 1.0
 * Cette classe represente un �tat d'un match de volley.
 * Elle contient : 
 * les actions attendus pour poursuivre un match.
 * Les positions des joueurs
 * Les joueurs qui ont �t� remplac�s
 * Le score
 * L'equipe qui a le service
 * Le set en cours
 * Le nombre de set gagn� par chacune des equipes
 * Un booleen indiquant si le match est fini (et que l'on se trouve dans un �tat final)
 */
public class VolleyState {
	private SparseArray<Boolean> allowedVolleyAction; 
	private SparseArray<Player> defTeamPosition;
	private SparseArray<Player> oppTeamPosition;
	private TreeMap<Integer, Integer> defTeamReplace;
	private TreeMap<Integer, Integer> oppTeamReplace;
	private short defTeamScore;
	private short oppTeamScore;
	private Team currentTeam;
	private short currentSet;
	private short defTeamWonSetCount;
	private short oppTeamWonSetCount;
	private static boolean matchEnded;
	private short defCountReplace;
	private short oppCountReplace;

	/**
	 * @param equipeDefensive
	 * @param equipeAdverse
	 * @param equipeActuel
	 * Instancie un etat de debut de match.
	 * Les joueurs sont plac�s dans l'ordre d'apparition dans equipeDefensive et equipeAdverse.
	 * equipeActuel doit �tre l'equipe ayant le service au d�but du match.
	 */
	public VolleyState(Team equipeDefensive, Team equipeAdverse, Team equipeActuel) {
		super();
		int pos1 = 1, pos2 = 1;
		this.currentTeam = equipeActuel;
		defTeamPosition = new SparseArray<Player>();
		oppTeamPosition = new SparseArray<Player>();
		defTeamReplace = new TreeMap<Integer, Integer>();
		oppTeamReplace = new TreeMap<Integer, Integer>();
		for(Player joueur: equipeDefensive.getPlayerlist()){
			defTeamPosition.put(pos1, joueur);
			pos1++;
			if(pos1 == 7){
				break;
			}
		}
		for(Player joueur: equipeAdverse.getPlayerlist()){
			oppTeamPosition.put(pos2, joueur);
			pos2++;
			if(pos2 == 7){
				break;
			}
		}
		defTeamScore = (short)0;
		oppTeamScore = (short)0;
		defCountReplace = (short)0;
		oppCountReplace = (short)0;
		currentSet = (short)1;
		matchEnded = false;
		defTeamWonSetCount = (short)0;
		oppTeamWonSetCount = (short)0;
		allowedVolleyAction = new SparseArray<Boolean>();
		allowedVolleyAction.put(31, true);
		allowedVolleyAction.put(32, true);
		allowedVolleyAction.put(33, true);
		allowedVolleyAction.put(34, true);
	}

	/**
	 * @param positionEquipeDefensive
	 * @param positionEquipeAdverse
	 * @param remplacementEquipeDefensive
	 * @param remplacementEquipeAdverse
	 * @param scoreEqDefensive
	 * @param scoreEqAdverse
	 * @param equipeActuel
	 * @param setEnCours
	 * @param nbSetGagneEqDefensive
	 * @param nbSetGagneEqAdverse
	 * @param actionVolleyPermise
	 * Instancie un �tat grace � la liste de tous ces arguments (utiliser pour fonctionnement interne).
	 * @see Dupliquer()
	 */
	private VolleyState(SparseArray<Player> positionEquipeDefensive,
			SparseArray<Player> positionEquipeAdverse,
			TreeMap<Integer, Integer> remplacementEquipeDefensive,
			TreeMap<Integer, Integer> remplacementEquipeAdverse,
			short scoreEqDefensive, short scoreEqAdverse, Team equipeActuel,
			short setEnCours, short nbSetGagneEqDefensive, short nbSetGagneEqAdverse,
			SparseArray<Boolean> actionVolleyPermise,
			short defCountReplace, short oppCountReplace){
		super();
		this.defTeamPosition = positionEquipeDefensive;
		this.oppTeamPosition = positionEquipeAdverse;
		this.defTeamReplace = remplacementEquipeDefensive;
		this.oppTeamReplace = remplacementEquipeAdverse;
		this.defTeamScore = scoreEqDefensive;
		this.oppTeamScore = scoreEqAdverse;
		this.currentTeam = equipeActuel;
		this.currentSet = setEnCours;
		this.defTeamWonSetCount = nbSetGagneEqDefensive;
		this.oppTeamWonSetCount = nbSetGagneEqAdverse;
		this.allowedVolleyAction = actionVolleyPermise;
		this.defCountReplace = defCountReplace;
		this.oppCountReplace = oppCountReplace;
	}

	/**
	 * @return EtatVolley
	 * Creer une copie parfaite de l'instance courante, et la renvoie.
	 */
	public VolleyState duplicate(){
		int i;
		int sizeActionPermise = allowedVolleyAction.size();
		SparseArray<Player> positionEquipeDefensiveClone = new SparseArray<Player>(6);
		SparseArray<Player> positionEquipeAdverseClone = new SparseArray<Player>(6);
		SparseArray<Boolean> actionVolleyPermiseClone = new SparseArray<Boolean>();
		TreeMap<Integer, Integer> remplacementEquipeDefensiveClone = new TreeMap<Integer, Integer>();
		TreeMap<Integer, Integer> remplacementEquipeAdverseClone = new TreeMap<Integer, Integer>();
		for(i = 1; i<=6; i++){
			positionEquipeDefensiveClone.put(i, defTeamPosition.get(i));
			positionEquipeAdverseClone.put(i, oppTeamPosition.get(i));
		}
		for(i=0; i < sizeActionPermise; i++){
			actionVolleyPermiseClone.put(allowedVolleyAction.keyAt(i), allowedVolleyAction.valueAt(i));
		}
		remplacementEquipeDefensiveClone.putAll(defTeamReplace);
		remplacementEquipeAdverseClone.putAll(oppTeamReplace);	
		return (new VolleyState(positionEquipeDefensiveClone, positionEquipeAdverseClone, 
				remplacementEquipeDefensiveClone, remplacementEquipeAdverseClone,
				defTeamScore, oppTeamScore, currentTeam, currentSet,
				defTeamWonSetCount, oppTeamWonSetCount, actionVolleyPermiseClone,
				defCountReplace, oppCountReplace));
	}

	/**
	 * @return Player
	 * Permet de recuperer le joueur qui a le service.
	 */
	public Player getServicePlayer(){
		Player res = null;
		if(currentTeam.haveIdPlayer(defTeamPosition.get(1).getId())) {
			res = defTeamPosition.get(1);
		}
		if(currentTeam.haveIdPlayer(oppTeamPosition.get(1).getId())) {
			res = oppTeamPosition.get(1);
		}
		return res;
	}

	/**
	 * @param team
	 * @return ArrayList<Player>
	 * Recupere les joueurs qui sont dans l'equipe team mais pas sur le terrain.
	 */
	public ArrayList<Player> getRemplacePlayer(Team team){
		int i;
		boolean isDefTeam = true;
		ArrayList<Player> res = new ArrayList<Player>(team.getPlayerlist());
		for(i=0; i<defTeamPosition.size(); i++){
			if(!team.haveIdPlayer(defTeamPosition.valueAt(i).getId())){
				isDefTeam = false;
				break;
			}
		}
		if(isDefTeam){
			for(i=0; i<defTeamPosition.size(); i++){
				res.remove(defTeamPosition.valueAt(i));
			}
		}
		else{
			for(i=0; i<oppTeamPosition.size(); i++){
				res.remove(oppTeamPosition.valueAt(i));
			}
		}
		return res;
	}

	/**
	 * Ajoute un point a l'equipe defensive.
	 * Cette fonction modifie le set en cours, le nombre de set gagne par l'equipe defensive.
	 * Cette fonction definie aussi la fin du match (et de l'automate).
	 */
	public void addPointToDefTeam(){
		defTeamScore++;
		if((defTeamScore >= 25 ) && (defTeamScore >= oppTeamScore+2)){
			oppCountReplace = 0;
			defCountReplace = 0;
			currentSet++;
			defTeamWonSetCount++;
			defTeamScore = 0;
			oppTeamScore = 0;
			if(defTeamWonSetCount == 3){
				matchEnded = true;
			}
		}
		if((defTeamScore >= 15) && (currentSet == 5) && (defTeamScore >= oppTeamScore+2)){
			defTeamWonSetCount++;
			matchEnded = true;
		}
	}

	/**
	 * Ajoute un point a l'equipe adverse.
	 * Cette fonction modifie le set en cours, le nombre de set gagne par l'equipe adverse.
	 * Cette fonction definie aussi la fin du match (et de l'automate).
	 */
	public void addPointToOppTeam(){
		oppTeamScore++;
		if((oppTeamScore >= 25 ) && (oppTeamScore >= 2+defTeamScore)){
			oppCountReplace = 0;
			defCountReplace = 0;
			currentSet++;
			oppTeamWonSetCount++;
			oppTeamScore = 0;
			defTeamScore = 0;
			if(oppTeamWonSetCount == 3){
				matchEnded = true;
			}
		}
		if((oppTeamScore >= 15) && (currentSet == 5) && (oppTeamScore >= 2+defTeamScore)){
			oppTeamWonSetCount++;
			matchEnded = true;
		}
	}

	/**
	 * @param allowedAction
	 * Definit les actions permises grace � allowedAction.
	 */
	public void defineAllowedAction(ArrayList<Integer> allowedAction){
		this.allowedVolleyAction.clear();
		for(int i : allowedAction){
			this.allowedVolleyAction.put(i, true);
		}
	}

	/**
	 * @return ArrayList<Integer>
	 * Retourne une liste des id des actions possibles.
	 */
	public ArrayList<Integer> listAllowAction(){
		int i;
		ArrayList<Integer> res = new ArrayList<Integer>();
		for(i=0; i<allowedVolleyAction.size(); i++){
			res.add(allowedVolleyAction.keyAt(i));
		}
		return res;
	}

	/**
	 * @param idAction
	 * @return boolean
	 * Indique si l'action d'id idAction est permise.
	 */
	public boolean isPermitted(int idAction){
		return allowedVolleyAction.get(idAction, false);
	}

	/**
	 * Fait une rotation de l'equipe defensive (respectant les regles du volley)
	 */
	public void defTeamRotate(){
		Player joueur = defTeamPosition.get(1);
		for(int i = 2; i < 7 ; i++){
			int y=i-1;
			defTeamPosition.put(y, defTeamPosition.get(i));
		}
		defTeamPosition.put(6, joueur);
	}

	/**
	 * Fait une rotation de l'equipe adverse (respectant les regles du volley)
	 */
	public void oppTeamRotate(){
		Player joueur = oppTeamPosition.get(1);
		for(int i = 2; i < 7 ; i++){
			int y=i-1;
			oppTeamPosition.put(y, oppTeamPosition.get(i));
		}
		oppTeamPosition.put(6, joueur);
	}

	/**
	 * @param replacedPlayer
	 * @param replacingPlayer
	 * @return VolleyStateMachineException.Reason
	 * Effectue un remplacement de joueur replacedPlayer par le joueur replacingPlayer dans l'equipe defensive.
	 * Si l'operation est impossible, renvoie la raison, sinon null
	 */
	public VolleyStateMachineException.Reason defTeamReplacePlayer(Player replacedPlayer, Player replacingPlayer){
		int pos = 0;
		if(defCountReplace >= 6){
			// On ne peux pas avoir plus de 6remplacements
			return VolleyStateMachineException.Reason.already6Replacement;
		}
		if(defTeamReplace.containsKey(replacedPlayer.getId())){
			// Le joueur a dej� �t� remplac�.
			return VolleyStateMachineException.Reason.playerAlreadyReplaced;
		}
		for(Map.Entry<Integer, Integer> entry : defTeamReplace.entrySet()){
			if(entry.getValue() == replacedPlayer.getId()){
				if(entry.getKey() == replacingPlayer.getId()){
					defCountReplace++;
					defTeamReplace.put(replacedPlayer.getId(), replacingPlayer.getId());
					pos = defTeamPosition.keyAt(defTeamPosition.indexOfValue(replacedPlayer));
					defTeamPosition.put(pos, replacingPlayer);
					return null;
				}
				else {
					return VolleyStateMachineException.Reason.replacingPlayerCanOnlyBeReplacedByReplacedPlayer;
				}
			}
		}
		// Si on est arriv� jusque l� c'est bon
		defCountReplace++;
		defTeamReplace.put(replacedPlayer.getId(), replacingPlayer.getId());	
		pos = defTeamPosition.keyAt(defTeamPosition.indexOfValue(replacedPlayer));
		defTeamPosition.put(pos, replacingPlayer);
		return null;
	}

	/**
	 * @param replacedPlayer
	 * @param replacingPlayer
	 * @return VolleyStateMachineException.Reason
	 * Effectue un remplacement de joueur replacedPlayer par le joueur replacingPlayer dans l'equipe adverse.
	 * Si l'operation est impossible, renvoie la raison, sinon null
	 */
	public VolleyStateMachineException.Reason oppTeamReplacePlayer(Player replacedPlayer, Player replacingPlayer){
		int pos = 0;
		if(oppCountReplace >= 6){
			// On ne peux pas avoir plus de 6remplacements
			return VolleyStateMachineException.Reason.already6Replacement;
		}
		if(oppTeamReplace.containsKey(replacedPlayer.getId())){
			// Le joueur a dej� �t� remplac�.
			return VolleyStateMachineException.Reason.playerAlreadyReplaced;
		}
		for(Map.Entry<Integer, Integer> entry : oppTeamReplace.entrySet()){
			if(entry.getValue() == replacedPlayer.getId()){
				if(entry.getKey() == replacingPlayer.getId()){
					oppCountReplace++;
					oppTeamReplace.put(replacedPlayer.getId(), replacingPlayer.getId());
					pos = oppTeamPosition.keyAt(oppTeamPosition.indexOfValue(replacedPlayer));
					oppTeamPosition.put(pos, replacingPlayer);
					return null;
				}
				else {
					return VolleyStateMachineException.Reason.replacingPlayerCanOnlyBeReplacedByReplacedPlayer;
				}
			}
		}
		// Si on est arriv� jusque l� c'est bon
		oppCountReplace++;
		oppTeamReplace.put(replacedPlayer.getId(), replacingPlayer.getId());
		pos = oppTeamPosition.keyAt(oppTeamPosition.indexOfValue(replacedPlayer));
		oppTeamPosition.put(pos, replacingPlayer);
		return null;
	}

	/**
	 * @return SparseArray<Joueur>
	 * Recupere la position de l'equipe defensive.
	 */
	public SparseArray<Player> getDefTeamPosition() {
		return defTeamPosition;
	}

	/**
	 * @param positionEquipeDefensive
	 * Defini la position de l'equipe defensive � positionEquipeDefensive (� eviter).
	 * @see rotationEqDef
	 */
	public void setDefTeamPosition(SparseArray<Player> positionEquipeDefensive) {
		this.defTeamPosition = positionEquipeDefensive;
	}

	/**
	 * @return SparseArray<Joueur>
	 * Recupere la position de l'equipe adverse.
	 */
	public SparseArray<Player> getOppTeamPosition() {
		return oppTeamPosition;
	}

	/**
	 * @param positionEquipeAdverse
	 * Defini la position de l'equipe adverse � positionEquipeAdverse (� eviter).
	 * @see rotationEqAdv
	 */
	public void setOppTeamPosition(SparseArray<Player> positionEquipeAdverse) {
		this.oppTeamPosition = positionEquipeAdverse;
	}

	/**
	 * @return short
	 * Recupere le set en cours.
	 */
	public short getCurrentSet() {
		return currentSet;
	}

	/**
	 * @param setEnCours
	 * Defini le set en cours � setEnCours (� eviter).
	 */
	public void setCurrentSet(short setEnCours) {
		this.currentSet = setEnCours;
	}

	/**
	 * @return TreeMap<Integer, Integer>
	 * Recupere les remplacements de l'equipe defensive.
	 */
	public TreeMap<Integer, Integer> getDefTeamReplace() {
		return defTeamReplace;
	}

	/**
	 * @param remplacementEquipeDefensive
	 * Defini les remplacements de l'equipe defensive � remplacementEquipeDefensive (� eviter).
	 * @see remplacerEqDef
	 */
	public void setDefTeamReplace(TreeMap<Integer, Integer> remplacementEquipeDefensive) {
		this.defTeamReplace = remplacementEquipeDefensive;
	}

	/**
	 * @return TreeMap<Integer, Integer>
	 * Recupere les remplacements de l'equipe adverse.
	 */
	public TreeMap<Integer, Integer> getOppTeamReplace() {
		return oppTeamReplace;
	}

	/**
	 * @param remplacementEquipeAdverse
	 * Defini les remplacements de l'equipe adverse � remplacementEquipeAdverse (� eviter).
	 * @see remplacerEqDef
	 */
	public void setOppTeamReplace(TreeMap<Integer, Integer> remplacementEquipeAdverse) {
		this.oppTeamReplace = remplacementEquipeAdverse;
	}

	/**
	 * @return Equipe
	 * Recupere l'equipe qui � le service.
	 */
	public Team getCurrentTeam() {
		return currentTeam;
	}

	/**
	 * @param equipeActuel
	 * Defini l'equipe qui � le service � equipeActuel (� eviter, le fonctionnement interne gere �a).
	 */
	public void setCurrentTeam(Team equipeActuel) {
		this.currentTeam = equipeActuel;
	}

	/**
	 * @return short
	 * Recupere le score actuel par l'equipe defensive.
	 */
	public short getDefTeamScore() {
		return defTeamScore;
	}

	/**
	 * @param scoreEqDefensive
	 * Defini le score de l'equipe defensive � scoreEqDefensive (� eviter, le fonctionnement interne gere �a).
	 */
	public void setDefTeamScore(short scoreEqDefensive) {
		this.defTeamScore = scoreEqDefensive;
	}

	/**
	 * @return short
	 * Recupere le score actuel par l'equipe adverse.
	 */
	public short getOppTeamScore() {
		return oppTeamScore;
	}

	/**
	 * @param scoreEqAdverse
	 * Defini le score de l'equipe adverse � scoreEqAdverse (� eviter, le fonctionnement interne gere �a).
	 */
	public void setOppTeamScore(short scoreEqAdverse) {
		this.oppTeamScore = scoreEqAdverse;
	}

	/**
	 * @return boolean
	 * Verifie si le match est fini. i.e. que l'�tat est un �tat final.
	 */
	public static boolean isMatchEnded() {
		return matchEnded;
	}

	/**
	 * @param matchFini
	 * Si matchFini est vrai, defini que l'�tat est un �tat final, i.e que l'automate dois s'arr�ter.
	 */
	public static void setMatchEnded(boolean matchFini) {
		VolleyState.matchEnded = matchFini;
	}

	/**
	 * @return short
	 * Recupere la nombre de set gagne par l'equipe defensive.
	 */
	public short getDefTeamWonSetCount() {
		return defTeamWonSetCount;
	}

	/**
	 * @param nbSetGagneEqDefensive
	 * Defini le nombre de set gagne par l'equipe defensive � nbSetGagneEqDefensive (� eviter, le fonctionnement interne gere �a).
	 */
	public void setDefTeamWonSetCount(short nbSetGagneEqDefensive) {
		this.defTeamWonSetCount = nbSetGagneEqDefensive;
	}

	/**
	 * @return short
	 * Recupere la nombre de set gagne par l'equipe adverse.
	 */
	public short getOppTeamWonSetCount() {
		return oppTeamWonSetCount;
	}

	/**
	 * @param nbSetGagneEqAdverse
	 * Defini le nombre de set gagne par l'equipe adverse � nbSetGagneEqAdverse (� eviter, le fonctionnement interne gere �a).
	 */
	public void setOppTeamWonSetCount(short nbSetGagneEqAdverse) {
		this.oppTeamWonSetCount = nbSetGagneEqAdverse;
	}

	/**
	 * @return String
	 * Renvoie une chaine de caractere decrivant l'�tat.
	 */
	public String toString(){
		int i;
		String res = "";
		res += "Position equipe defensive ---\n";
		for(i=1; i<=6; i++){
			res += (Integer.toString(i) + " : " + defTeamPosition.get(i).getName() + " ");
		}
		res += "\nPosition equipe adverse ---\n";
		for(i=1; i<=6; i++){
			res += (Integer.toString(i) + ":" + oppTeamPosition.get(i).getName() + " ");
		}
		res += ("\nScore : " + Short.toString(defTeamScore) + "/" + Short.toString(oppTeamScore));
		res += ("\nEquipe en cours : " + currentTeam.getName());
		res += ("\nSet en cours : " + Short.toString(currentSet));
		res += ("\nSet gagne par l'equipe defensive : " + Short.toString(defTeamWonSetCount));
		res += ("\nSet gagne par l'equipe adverse : " + Short.toString(oppTeamWonSetCount));
		if(matchEnded){
			res += ("\nMatch fini");
		}
		else{
			res += ("\nMatch en cours");
		}
		return res;
	}
}
